from google.appengine.ext import db
from google.appengine.ext.db import polymodel
from datetime import date, datetime, timedelta
from horseanalysis.model.race_batch import RaceBatch
from horseanalysis.model.win_place import WinPlace

ADMIN_COST = 0.125

class ProbabilityResultBase(polymodel.PolyModel):
  race_batch = db.ReferenceProperty(RaceBatch)
  probability = db.FloatProperty()
  odds = db.FloatProperty()
  expected_return = db.FloatProperty()
  gain_offset = db.FloatProperty()
  
  def get_bet(self, betting_scheme):
    return betting_scheme.get_expected_bet(self.expected_return)
  
  def get_result(self, betting_scheme):
    if self.gain_offset:
      return betting_scheme.get_expected_bet(self.expected_return) * self.gain_offset
    else:
      return 0
  
  def update_gain_offset(self, results):
    raise NotImplementedError("Subclass must implement abstract method")
  
  @classmethod
  def get_by_expected_return(cls, race_batch, from_expected_return):
    return cls.gql("WHERE race_batch = :1 and expected_return > :2 order by expected_return desc", race_batch, from_expected_return)
  
  @classmethod
  def get_by_race_batch(cls, race_batch):
    return cls.gql("WHERE race_batch = :1", race_batch)
  
  @classmethod
  def prerequisites_checking(cls, race_batch):
    odds_collection = WinPlace.get_win_odds(race_batch)
    for odds in odds_collection:
      if odds_collection[odds] == 0:
        return False
    return race_batch.win_pool > 0 and len(odds_collection) > 1
    
  @classmethod
  def cal_win_prob(cls, race_batch):
    actual_win_pool = race_batch.win_pool
    hypothetical_win_pool = 0.0
    result = {}
    pool_result = {}
    odds_collection = WinPlace.get_odds(race_batch)
    
    for odds in odds_collection:
      pool_result[odds.horse_no] = (1-ADMIN_COST)/odds.win*actual_win_pool
      
    for horse_win_pool in pool_result:
      hypothetical_win_pool += pool_result[horse_win_pool]
      
    for odds in odds_collection:
      result[odds.horse_no] = pool_result[odds.horse_no]/hypothetical_win_pool

    return result
  
  @classmethod
  def cal_individual_place_prob(cls, win_prob, i, j, k):
    pi = win_prob[i]
    pj = win_prob[j]
    pk = win_prob[k]
    return (pi * pj * pk) / ((1-pi)*(1-pi-pj))
  
  @classmethod
  def cal_individual_quinella_prob(cls, win_prob, i, j):
    pi = win_prob[i]
    pj = win_prob[j]
    return (pi * pj) / (1-pi)
  
  @classmethod
  def is_result_analyzied_in_batch(cls, race_batch):
    query = cls.gql("WHERE race_batch = :1", race_batch)
    if query.count() > 0:
      return True
    else:
      return False
  
  @classmethod
  def analyze_batch_if_not_existed(cls, race_batch):
    items = cls.get_by_race_batch(race_batch)
    if items.count() == 0:
      cls.__analyze_batch__(race_batch)
    
  @classmethod
  def __analyze_batch__(cls, race_batch):
    raise NotImplementedError("Subclass must implement abstract method")
  
  @classmethod
  def complete_related_race_batch_probability_result(cls, race_batch):
    raise NotImplementedError("Subclass must implement abstract method")
  
  @classmethod
  def incomplete_related_race_batch_probability_result(cls, race_batch):
    raise NotImplementedError("Subclass must implement abstract method")
  
  @classmethod
  def update_all_gain_offset_if_have_result(cls, race_batch):
    results = race_batch.race.get_first_three_results()
    if len(results) > 0:
      items = cls.get_by_race_batch(race_batch)
      for item in items:
        item.update_gain_offset(results)
      cls.complete_related_race_batch_probability_result(race_batch)
      return True
    else: 
      return False